﻿using System;
using System.Text;
using PengSW.NotifyPropertyChanged;
using PengSW.TcpService;
using static PengSW.RuntimeLog.RL;

namespace ProtocolSpy
{
    class MainModel : NotifyPropertyChangedObject
    {
        public int ListenPort { get => _ListenPort; set { SetValue(ref _ListenPort, value, nameof(ListenPort)); } }
        private int _ListenPort = 10001;

        public string TargetHost { get => _TargetHost; set { SetValue(ref _TargetHost, value, nameof(TargetHost)); } }
        private string _TargetHost = "127.0.0.1";

        public int TargetPort { get => _TargetPort; set { SetValue(ref _TargetPort, value, nameof(TargetPort)); } }
        private int _TargetPort = 10001;

        public Encoding Encoding { get => _Encoding; set { SetValue(ref _Encoding, value, nameof(Encoding)); } }
        private Encoding _Encoding = _Encodings[0];

        public Encoding[] Encodings => _Encodings;

        private readonly static Encoding[] _Encodings = new Encoding[] { Encoding.UTF8, Encoding.UTF7, Encoding.UTF32, Encoding.Unicode, Encoding.ASCII, Encoding.Default, Encoding.BigEndianUnicode };

        #region Upper Connection -- Spy Server

        private Listener _Listener;

        public bool CanStartListen => !(_Listener?.IsListening ?? false);

        public void StartListen()
        {
            _Listener = new Listener("Spy Server", ListenPort, null);
            _Listener.Clarify += OnListener_Clarify;
            _Listener.Connected += OnUpperConnection_Connected;
            _Listener.Disconnected += OnUpperConnection_Disconnected;
            _Listener.Received += OnUpperConnection_Received;
            _Listener.StartListen();
        }

        public bool CanStopListen => !CanStartListen;

        public void StopListen()
        {
            if (_Listener == null) return;
            _Listener.StopListen();
            _Listener.Clarify -= OnListener_Clarify;
            _Listener.Connected -= OnUpperConnection_Connected;
            _Listener.Disconnected -= OnUpperConnection_Disconnected;
            _Listener.Received -= OnUpperConnection_Received;
            _Listener = null;
        }

        private void OnUpperConnection_Received(Connection aConnection, byte[] aBytes)
        {
            try
            {
                string aText = _Encoding.GetString(aBytes);
                L($"[UpperConnection Received]{aText}");
                if (_LowerConnection?.IsConnected ?? false)
                {
                    _LowerConnection.SendBytes(aBytes);
                }
            }
            catch (Exception ex)
            {
                E(ex, "OnUpperConnectionReceived");
            }
        }

        private void OnUpperConnection_Disconnected(Listener aListener, Connection aConnection)
        {
            L($"UpperConnection[{aConnection}] is disconnected.");
        }

        private void OnUpperConnection_Connected(Listener aListener, Connection aConnection)
        {
            L($"UpperConnection[{aConnection}] is connected.");
            if (CanConnectToTargetServer)
            {
                try
                {
                    ConnectTargetServer();
                }
                catch (Exception ex)
                {
                    E(ex, "ConnectToTargetServer");
                }
            }
        }

        private void OnListener_Clarify(string aInfo, int aLevel)
        {
            L($"[Spy Server]{aInfo}", aLevel);
        }

        #endregion

        #region Lower Connection -- Spy Client

        private Connection _LowerConnection;

        public bool CanConnectToTargetServer => !(_LowerConnection?.IsConnected ?? false) && !string.IsNullOrWhiteSpace(TargetHost);

        public void ConnectTargetServer()
        {
            _LowerConnection = new Connection(null, "Spy Client");
            _LowerConnection.Received += OnLowerConnection_Received;
            _LowerConnection.Clarify += OnLowerConnection_Clarify;
            _LowerConnection.Connected += OnLowerConnection_Connected;
            _LowerConnection.Disconnected += OnLowerConnection_Disconnected;
            _LowerConnection.WorkWith(TargetHost, TargetPort);
        }

        private void OnLowerConnection_Disconnected(object sender, System.EventArgs e)
        {
            L($"LowerConnection[{_LowerConnection}] is disconnected.");
            _LowerConnection?.Dispose();
            _LowerConnection = null;
        }

        private void OnLowerConnection_Connected(object sender, System.EventArgs e)
        {
            L($"LowerConnection[{_LowerConnection}] is connected.");
        }

        private void OnLowerConnection_Clarify(Connection aConnection, string aInfo, int aLevel)
        {
            L($"[LowerConnection Clarify]{aInfo}", aLevel);
        }

        private void OnLowerConnection_Received(Connection aConnection, byte[] aBytes)
        {
            try
            {
                string aText = _Encoding.GetString(aBytes);
                L($"[LowerConnection Received]{aText}");
                _Listener.SayToAll(aBytes);
            }
            catch (Exception ex)
            {
                E(ex, "OnLowerConnectionReceived");
            }
        }

        #endregion
    }
}
